﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Apewer
{

    /// <summary>数组构建器。</summary>
    public sealed class ArrayBuilder<T>
    {

        private Type _type = typeof(T);

        private T[] _array;
        private int _capacity;
        private int _count;
        private int _step;

        /// <summary>创建构建程序实例。</summary>
        /// <param name="step">每次扩容的增量，最小值：1，默认值：256。</param>
        /// <exception cref="ArgumentNullException"></exception>
        public ArrayBuilder(int step = 256)
        {
            if (step < 1) throw new ArgumentOutOfRangeException(nameof(step));
            _step = step;
            _capacity = step;
            _count = 0;
            _array = new T[step];
        }

        private ArrayBuilder(ArrayBuilder<T> old)
        {
            _step = old._step;
            _capacity = old._capacity;
            _count = old._count;
            _array = new T[_capacity];
            if (_count > 0) Array.Copy(old._array, _array, _count);
        }

        /// <summary>获取或设置指定位置的元素，索引器范围为 [0, Length)。</summary>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= _count) throw new ArgumentOutOfRangeException("索引超出了当前数组的范围。");
                return _array[index];
            }
            set
            {
                if (index < 0 || index >= _count) throw new ArgumentOutOfRangeException("索引超出了当前数组的范围。");
                _array[index] = value;
            }
        }

        /// <summary>源数组。</summary>
        public T[] Origin { get => _array; }

        /// <summary>缓冲区的容量。</summary>
        public int Capacity { get => _capacity; }

        /// <summary>当前的元素数量。</summary>
        public int Length { get => _count; }

        /// <summary>当前的元素数量。</summary>
        public int Count { get => _count; }

        /// <summary>指定元素在数组中的偏移位置。</summary>
        public int IndexOf(T value)
        {
            var array = _array;
            var length = _count;
            if (length < 1) return -1;
            for (var i = 0; i < length; i++)
            {
                var item = array[i];
                if (item == null) continue;
                if (item.Equals(value)) return i;
            }
            return -1;
        }

        /// <summary>已包含指定的元素。</summary>
        public bool Contains(T value) => IndexOf(value) > -1;

        /// <summary>添加元素。</summary>
        public void Add(T item)
        {
            if (_capacity - _count < 1)
            {
                _capacity += _step;
                var temp = new T[_capacity];
                Array.Copy(_array, temp, _count);
                _array = temp;
            }
            _array[_count] = item;
            _count++;
        }

        /// <summary>添加多个元素。</summary>
        public void Add(params T[] items)
        {
            if (items == null) return;

            var length = items.Length;
            if (length < 1) return;

            if (_capacity - _count < length)
            {
                _capacity = _count + length;
                var temp = new T[_capacity];
                Array.Copy(_array, temp, _count);
                _array = temp;
            }

            Array.Copy(items, 0, _array, _count, length);
            _count += length;
        }

        /// <summary>添加多个元素。</summary>
        public void Add(ICollection<T> items)
        {
            if (items == null) return;

            var length = items.Count;
            if (length < 1) return;

            if (_capacity - _count < length)
            {
                _capacity = _count + length;
                var temp = new T[_capacity];
                Array.Copy(_array, temp, _count);
                _array = temp;
            }

            items.CopyTo(_array, _count);
            _count += length;
        }

        /// <summary>添加多个元素。</summary>
        public void Add(IEnumerable<T> items)
        {
            if (items == null) return;
            foreach (var item in items) Add(item);
        }

        /// <summary>添加元素。</summary>
        /// <param name="buffer">要添加的元素数组。</param>
        /// <param name="offset">buffer 的开始位置。</param>
        /// <param name="count">buffer 的元素数量。</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public void Add(T[] buffer, int offset, int count)
        {
            if (buffer == null) throw new ArgumentNullException(nameof(buffer));
            var length = buffer.Length;
            if (offset < 0 || offset >= length || count < 0) throw new ArgumentOutOfRangeException();
            if (offset + count > length) throw new ArgumentOutOfRangeException();

            if (_capacity - _count < length)
            {
                _capacity = _count + length;
                var temp = new T[_capacity];
                Array.Copy(_array, temp, _count);
                _array = temp;
            }

            Array.Copy(buffer, offset, _array, _count, count);
            _count += count;
        }

        /// <summary>清空所有元素。</summary>
        public void Clear()
        {
            _capacity = 0;
            _count = 0;
            _array = new T[0];
        }

        // 修剪数组，去除剩余空间。
        void Trim()
        {
            if (_count == 0)
            {
                if (_capacity > 0)
                {
                    _capacity = 0;
                    _array = new T[0];
                }
                return;
            }

            if (_count == _capacity) return;

            var array = new T[_count];
            Array.Copy(_array, array, _count);
            _capacity = _count;
        }

        #region 模仿 JavaScript 数组方法。

        /// <summary>添加元素。</summary>
        public void Push(T item) => Add(item);

        /// <summary>取出最后一个元素。</summary>
        public T Pop(Func<T> @default = null)
        {
            if (_count > 0)
            {
                var offset = _count - 1;
                var value = _array[offset];
                _array[offset] = default(T);
                _count -= 1;
                return value;
            }
            return @default == null ? default(T) : @default.Invoke();
        }

        /// <summary>取出第一个元素，当数组为空时使用指定方法返回默认值。</summary>
        public T Shift(Func<T> @default = null)
        {
            if (_count > 0)
            {
                var value = _array[0];
                var temp = new T[_count];
                if (_count > 1) Array.Copy(_array, 1, temp, 0, _count - 1);
                _array = temp;
                _count -= 1;
                return value;
            }
            return @default == null ? default(T) : @default.Invoke();
        }

        /// <summary>添加元素到数组最前端。</summary>
        public void Unshift(T item)
        {
            var remains = _capacity - _count;
            var length = remains > 0 ? _capacity : (_capacity + _step);

            var temp = new T[length];
            temp[0] = item;
            if (_count > 0)
            {
                Array.Copy(_array, 0, temp, 1, _count);
                _count++;
            }
            _capacity = length;
            _array = temp;
        }

        #endregion

        #region 导出、转换。

        /// <summary>导出字符串。</summary>
        public override string ToString() => ToString(null);

        /// <summary>导出字符串。</summary>
        /// <param name="separator">分隔符。</param>
        public string ToString(string separator)
        {
            var sb = new StringBuilder();
            var hasSeparator = !string.IsNullOrEmpty(separator);
            if (_type.IsValueType)
            {
                for (var i = 0; i < _count; i++)
                {
                    if (i > 0 && hasSeparator) sb.Append(separator);
                    sb.Append(_array[i].ToString());
                }
            }
            else
            {
                var isString = _type == typeof(string);
                for (var i = 0; i < _count; i++)
                {
                    if (_array[i] == null) continue;
                    if (i > 0 && hasSeparator) sb.Append(separator);
                    sb.Append(isString ? _array[i] as string : _array[i].ToString());
                }
            }
            return sb.ToString();
        }

        /// <summary>导出到新数组。</summary>
        /// <param name="clear">导出后清空元素。</param>
        public T[] Export(bool clear = false)
        {
            if (_count == 0) return new T[0];
            if (_count == _capacity) return _array;
            var array = new T[_count];
            Array.Copy(_array, array, _count);
            if (clear) _array = new T[0];
            return array;
        }

        /// <summary>克隆当前实例，生成新实例。</summary>
        public ArrayBuilder<T> Clone() => new ArrayBuilder<T>(this);

        /// <summary>使用 Export 方法实现从 ArrayBuilder&lt;T&gt; 到 T[] 的隐式转换。</summary>
        public static implicit operator T[](ArrayBuilder<T> instance) => instance == null ? null : instance.Export();

        internal static string Text(ArrayBuilder<char> ab)
        {
            if (ab == null) return "";
            if (ab._count < 1) return "";
            return new string(ab._array, 0, ab._count);
        }

        internal static string Text(ArrayBuilder<char> ab, int start, int count)
        {
            if (start < 0) throw new ArgumentOutOfRangeException("startIndex", "起始位置不可小于 0。");
            if (count < 0) throw new ArgumentOutOfRangeException("maxCount", "最大数量不可小于 0。");

            if (ab == null || count < 1) return "";
            if (start < 0) start = 0;
            var total = ab._count;
            if (start + count > total) count = total - start;
            if (count < 1) return "";
            return new string(ab._array, start, count);
        }

        internal static string Join(ArrayBuilder<string> ab, string separator)
        {
            if (ab == null) return "";
            return Join(ab, separator, 0, ab._count);
        }

        internal static string Join(ArrayBuilder<string> ab, string separator, int start, int count)
        {
            if (start < 0) throw new ArgumentOutOfRangeException("startIndex", "起始位置不可小于 0。");
            if (count < 0) throw new ArgumentOutOfRangeException("maxCount", "最大数量不可小于 0。");

            if (ab == null || count < 1) return ""; // 数组为空。
            if (start < 0) start = 0; // 修正起始位置最小值。
            var total = ab._count; // 数组中的元素总数。
            if (start + count > total) count = total - start; // 最大数量。
            if (count < 1) return ""; // 不取任何元素。

            var end = start + count; // 结束位置。
            var se = string.IsNullOrEmpty(separator); // 分隔符为空。
            var sb = new StringBuilder();
            for (var i = start; i < end; i++)
            {
                var item = ab._array[i];
                if (i > start && !se) sb.Append(separator);
                if (item == null || item == "") continue;
                sb.Append(item);
            }
            return sb.ToString();
        }

        #endregion

    }

}
